package com.ft.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ft.dao.NoteDao;
import com.ft.domain.Note;
import com.ft.service.INoteService;
import com.ft.utils.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson.JSON;

/**
 * @Author: ft
 * @Date: 2023/02/09/21:32
 * @Description:
 */
@Service
public class NoteServiceImpl extends ServiceImpl<NoteDao, Note> implements INoteService {
    @Autowired
    private NoteDao noteDao;
    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public int addNote(Note note) throws IllegalAccessException {
        //一天可以放入的次数
        int num = 0;
        //1.先获取用户一天放入的次数
        Object totalnum = redisTemplate.opsForValue ().get ("oneDayCanAddNum:" + note.getIssueUserid ());
        if (totalnum == null) {
            num = 0;
        } else {
            num = (int) totalnum;
        }
        //一天可以放入的次数
        if (num < 3) {
            int i = noteDao.addNote (note);
            if (i == 1) {
                /**
                 * 先存redis,在存数据库，最后存数据库成功后进行校验，保证数据库和redis里都有数据并且一致
                 *  1.封装订单编号，存入tzmarketIdZset所有订单集合中
                 *  oldBuyOrderId: 获取添加Zset前的个数
                 *  id ：订单id
                 */
                Long oldsize;
                String id = String.valueOf (note.getId ());
                System.out.println ("进入了添加redis");
                if (note.getSex () == 1) {
                    oldsize = redisTemplate.opsForZSet ().zCard ("boyNoteId" + note.getIssueUserid ());
                    redisTemplate.opsForZSet ().add ("boyNoteId", id, 1);
                } else {
                    oldsize = redisTemplate.opsForZSet ().zCard ("girlNoteId" + note.getIssueUserid ());
                    redisTemplate.opsForZSet ().add ("girlNoteId", id, 1);
                }
                /**
                 *  3.封装订单信息，存入Hsah里
                 *  格式：订单类型 : 用户id : 订单号
                 *  orderKey1：封装hash的key值
                 *  stringObjectMap：得到对象属性的map集合
                 */
                String orderKey1 = "note:" + note.getId ();
                Map<String, Object> stringObjectMap = null;
                stringObjectMap = ObjectUtils.objectToMap (note);
                redisTemplate.opsForHash ().putAll (orderKey1, stringObjectMap);
                /**
                 * 4.将数据存入数据库
                 */
                /**
                 * 数据校验，保证数据一致性
                 * newBuyOrderId：获取添加后的所有订单的Zset个数
                 * newlistsize:添加后的用户未付款list集合长度
                 * hashOrderStr：获取存入redis的数据
                 * hashOrderSize ：订单表所有字段的和
                 */
                while (true) {
                    Long newsize;
                    if (note.getSex () == 1) {
                        newsize = redisTemplate.opsForZSet ().zCard ("boyNoteId");
                    } else {
                        newsize = redisTemplate.opsForZSet ().zCard ("girlNoteId");
                    }
                    Long hashOrderSize = redisTemplate.opsForHash ().size (orderKey1);
                    //结束条件
                    if (newsize > oldsize & hashOrderSize == Note.total) {
                        break;
                    }
                    //对存入订单id进行校验
                    if (newsize <= oldsize) {
                        if (note.getSex () == 1) {
                            redisTemplate.opsForZSet ().add ("boyNoteId", id, 1);
                        } else {
                            redisTemplate.opsForZSet ().add ("girlNoteId", id, 1);
                        }
                        continue;
                    }
                    //对hash订单表进行校验
                    if (hashOrderSize != Note.total) {
                        //移除key
                        redisTemplate.delete (orderKey1);
                        //重新添加
                        Map<String, Object> stringObjectMap1 = ObjectUtils.objectToMap (note);
                        Set mapkey1 = stringObjectMap.keySet ();
                        for (Object key : mapkey1) {
                            redisTemplate.opsForHash ().put (orderKey1, key, stringObjectMap.get (key));
                        }
                    }
                }
                //同步订单创建时间
                String time = noteDao.getcreateTime (note.getId ());
                System.out.println ("time:" + time);
                redisTemplate.opsForHash ().put (orderKey1, "createTime", time);
                //添加一天可以放入的次数
                redisTemplate.opsForValue ().set ("oneDayCanAddNum:" + note.getIssueUserid (), num + 1, 1, TimeUnit.DAYS);
                //添加到自己放入的纸条zset中
                redisTemplate.opsForZSet ().add ("putIntoNote:" + note.getIssueUserid (), id, 1);
                return 1;
            } else {
                return 0;
            }
        } else {//放入次数上限
            return 3;
        }
    }

    @Override
    public int deleteNote(Long id) {
        int i = noteDao.deleteNote (id);
        if (i == 1) {
            //删除redis中的hash
            Boolean delete = redisTemplate.delete ("node:" + id);
            //删除zset集合中的id
            if (noteDao.getNoteSex (id) == 1) {
                redisTemplate.boundZSetOps ("boynoteId").remove (id);
            } else {
                redisTemplate.boundZSetOps ("girlnoteId").remove (id);
            }
            return 1;
        }
        return 0;
    }

    @Override
    public int updateNote(String title, String content, Long id) {
        int i = noteDao.updateNote (title, content, id);
        if (i == 1) {
            int noteSex = noteDao.getNoteSex (id);
            //修改redis中的字段
            if (noteSex == 1) {
                redisTemplate.opsForHash ().delete ("boynote:", "title");
                redisTemplate.opsForHash ().put ("boynote:", "title", title);

                redisTemplate.opsForHash ().delete ("boynote:", "content");
                redisTemplate.opsForHash ().put ("boynote:", "content", content);
                return 1;
            } else {
                redisTemplate.opsForHash ().delete ("girlnote:", "title");
                redisTemplate.opsForHash ().put ("girlnote:", "title", title);

                redisTemplate.opsForHash ().delete ("girlnote:", "content");
                redisTemplate.opsForHash ().put ("girlnote:", "content", content);
                return 1;
            }
        }
        return 0;
    }

    @Override
    public String getNoteCreate(Long id) {
        String s = noteDao.getcreateTime (id);
        return s;
    }

    @Override
    public Note getNote(Long id) {
        Note noteById = noteDao.getNoteById (id);
        return noteById;
    }

    //获取用户放入的纸条
    @Override
    public List<Note> getNotes(int page, int userid) {
        //返回结果集合
        List result = new ArrayList ();
        //获取用户放入纸条数据
        Set buyorderId = redisTemplate.opsForZSet ().reverseRangeByScore ("putIntoNote:" + userid, 1, 999);
        //如果数据小于5的情况
        if (buyorderId.size () < 50) {
            List ids = new ArrayList<> (buyorderId);
            //获取订单数据返回
            for (int i = 0; i < ids.size (); i++) {
                Map entries = redisTemplate.opsForHash ().entries ("note:" + ids.get (i));
                //对订单号处理成字符串，防止前端精度丢失
                Object id = entries.get ("id");
                entries.put ("id", "" + id);
                result.add (entries);
            }
            return result;
        }
        //数据大于5的情况
        //将set集合转换成list
        List ids = new ArrayList<> (buyorderId);
        //每次返回5个数据，total：获取数据结束的位置
        int total = 0;
        //每次获取数据循环开始的位置
        int num = page * 5;
        if (page == 0) {
            total = 5;
        } else {
            total = (page + 1) * 5;
        }
        for (; num < total & num < buyorderId.size (); num++) {
            Map entries = redisTemplate.opsForHash ().entries ("note:" + ids.get (num));
            //对订单号处理成字符串，防止前端精度丢失
            Object id = entries.get ("id");
            entries.put ("id", "" + id);
            result.add (entries);
        }
        //获取分页数据
        return result;
    }

    //获取用户抽取纸条
    @Override
    public List<Note> getOutNotes(int page, int userid) {
        //返回结果集合
        List result = new ArrayList ();
        //获取用户放入纸条数据
        Set buyorderId = redisTemplate.opsForZSet ().reverseRangeByScore ("takeOutNote:" + userid, 1, 999);
        //如果数据小于5的情况
        if (buyorderId.size () < 50) {
            List ids = new ArrayList<> (buyorderId);
            //获取订单数据返回
            for (int i = 0; i < ids.size (); i++) {
                Map entries = redisTemplate.opsForHash ().entries ("note:" + ids.get (i));
                //对订单号处理成字符串，防止前端精度丢失
                Object id = entries.get ("id");
                entries.put ("id", "" + id);
                result.add (entries);
            }
            return result;
        }
        //数据大于5的情况
        //将set集合转换成list
        List ids = new ArrayList<> (buyorderId);
        //每次返回5个数据，total：获取数据结束的位置
        int total = 0;
        //每次获取数据循环开始的位置
        int num = page * 5;
        if (page == 0) {
            total = 5;
        } else {
            total = (page + 1) * 5;
        }
        for (; num < total & num < buyorderId.size (); num++) {
            Map entries = redisTemplate.opsForHash ().entries ("note:" + ids.get (num));
            //对订单号处理成字符串，防止前端精度丢失
            Object id = entries.get ("id");
            entries.put ("id", "" + id);
            result.add (entries);
        }
        //获取分页数据
        return result;
    }

    @Override
    public Long getnoteNumber(int T) {
        if (T == 0) {
            return redisTemplate.opsForZSet ().zCard ("girlnoteId");
        } else {
            return redisTemplate.opsForZSet ().zCard ("boynoteId");
        }
    }

    @Override
    public Long getNotenumBoy() {
        Long boynoteId = redisTemplate.opsForZSet ().zCard ("boyNoteId");
        if (boynoteId == 0) {
            return Long.valueOf (0);
        } else {
            return boynoteId;
        }
    }

    @Override
    public Long getNotenumGirl() {
        Long girlnoteId = redisTemplate.opsForZSet ().zCard ("girlNoteId");
        if (girlnoteId == 0) {
            return Long.valueOf (0);
        } else {
            return girlnoteId;
        }
    }

    /**
     * 随机获取一张纸条
     *
     * @param T 0，女生纸条，1男生纸条
     * @return
     */
    @Override
    public Note getNoteRandom(int T, int userid) {
        Note result = new Note ();
        int b = 0;
        Object t = redisTemplate.opsForValue ().get ("takeOutNum:"+userid);
        if (t != null) {
            b = (int) t;
        }
        if (t == null | b < 3) {
            //获取女生纸条
            if (T == 0) {
                //我放入的纸条
                Set myNoteIdSet = redisTemplate.opsForZSet ().reverseRangeByScore ("putIntoNote:"+userid,1,9);
                //所有纸条
                Set girlId = redisTemplate.opsForZSet ().reverseRangeByScore ("girlNoteId", 1, 9);
                if(girlId.size ()  - myNoteIdSet.size () <= 0){
                    return null;
                }
                List ids = new ArrayList<> (girlId);
                if (girlId.size () > 0) {
                    while (true) {
                        //随机的下标
                        int a = new Random ().nextInt (ids.size ());
                        Map entries = redisTemplate.opsForHash ().entries ("note:" + ids.get (a));
                        //是自己的纸条的话重新获取
                        if((int)entries.get("issueUserid") == userid){
                            continue;
                        }
                        //对订单号处理成字符串，防止前端精度丢失
                        Object id = entries.get ("id");
                        entries.put ("id", "" + id);
                        result = JSON.parseObject (JSON.toJSONString (entries), Note.class);
                        if (result.getIssueUserid () != userid) {
                            break;
                        } else {
                            result = null;
                        }
                    }
                    //删除redis中的数据
                    redisTemplate.boundZSetOps ("girlNoteId").remove ("" + result.getId ());
                    //添加到自己抽取的纸条zset中
                    redisTemplate.opsForZSet ().add ("takeOutNote:"+userid, result.getId (), 1);
                    //增加每天可以抽取的次数
                    redisTemplate.opsForValue ().set ("takeOutNum:"+userid,b+1 , 1, TimeUnit.DAYS);
                    return result;
                } else {
                    return result = null;
                }
            } else {
                //我放入的纸条
                Set myNoteIdSet = redisTemplate.opsForZSet ().reverseRangeByScore ("putIntoNote:"+userid,1,9);
                //所有纸条
                Set girlId = redisTemplate.opsForZSet ().reverseRangeByScore ("boyNoteId", 1, 9);
                if(girlId.size ()  - myNoteIdSet.size () <= 0){
                    return null;
                }
                List ids = new ArrayList<> (girlId);
                if (girlId.size () > 0) {
                    while (true) {
                        //随机的下标
                        int a = new Random ().nextInt (ids.size ());
                        Map entries = redisTemplate.opsForHash ().entries ("note:" + ids.get (a));
                        //是自己的纸条的话重新获取
                        if((int)entries.get("issueUserid") == userid){
                            continue;
                        }
                        //对订单号处理成字符串，防止前端精度丢失
                        Object id = entries.get ("id");
                        entries.put ("id", "" + id);
                        result = JSON.parseObject (JSON.toJSONString (entries), Note.class);
                        if (result.getIssueUserid () != userid) {
                            break;
                        } else {
                            result = null;
                        }
                    }
                    //删除redis中的数据
                    redisTemplate.boundZSetOps ("boyNoteId").remove ("" + result.getId ());
                    //添加到自己抽取的纸条zset中
                    redisTemplate.opsForZSet ().add ("takeOutNote:"+userid, result.getId (), 1);
                    //增加每天可以抽取的次数
                    redisTemplate.opsForValue ().set ("takeOutNum:"+userid,b+1 , 1, TimeUnit.DAYS);
                    return result;
                } else {
                    return result = null;
                }

            }
        } else {
            return null;
        }
    }
}
